import { inject, reactive } from 'vue';
import {
  createMutations,
  createActions,
  createGetters,
  createCommitFn,
  createDispatchFn
} from './creators';

class Store {
  constructor(options) {
    const { state, getters, mutations, actions } = options;
    const store = this;
    const { commit, dispatch } = store;

    store._state = reactive({ data: state });

    //创建没有继承链的空对象防止继承Object里的方法 内部方法隔离操作
    store._mutations = Object.create(null);
    store._actions = Object.create(null);

    //给store._mutations对象定义方法并加工执行方法
    createMutations(store, mutations);
    //给store._actions对象定义方法并加工执行方法
    createActions(store, actions);
    //给store.getters对象定义属性并代理获取最新的数据
    createGetters(store, getters);

    //绑定Store实例的方法并传入参数
    createCommitFn(store, commit);
    createDispatchFn(store, dispatch);

    /**
     console.log(this);
     * {
     *   commit:  ƒ (type, payload),
     *   dispatch: ƒ (type, payload),
     *   getters: {},
     *   _actions: {addTodo: ƒ, toggleTodo: ƒ, removeTodo: ƒ},
     *   _mutations: {addTodo: ƒ, toggleTodo: ƒ, removeTodo: ƒ, setFilter: ƒ},
     *   _state: Proxy {data: {…}},
     *   state: (...)
     * }
     */
  }

  //所有原型方法都是静态的，这些方法都不会经过实例化
  //Store.prototype.install
  install(app) {
    //将store实例提供给外部使用,如给useStore函数使用
    app.provide('store', this);
    //当vue所有组件视图$store.state访问时挂载全局变量实现视图可以访问实例
    app.config.globalProperties.$store = this;
  }

  //开发者访问代理操作
  //如访问 store.state.xxx时代理至当前实例指定的属性
  get state() {
    return this._state.data; //响应式数据
  }

  /**
   * 如果用函数声明的方式定义方法，当开发者用解构写法解构{commit}时，会存在this指向丢失的问题，本来指向实例变为undefined
   * commit(type, payload) {}
   *
   * 解决办法:
   * 1. 箭头函数定义
   * commit = () => {}
   * 2. constructor内绑定this指向
   * this.commit = this.commit.bind(this);
   */
  commit(type, payload) {
    //this._mutations.addTodo(payload);
    this._mutations[type](payload);
  }

  dispatch(type, payload) {
    this._actions[type](payload);
  }
}

export function createStore(options) {
  return new Store(options);
}

export function useStore() {
  //获取store实例并返回供全局use注册
  return inject('store');
}
